今天我們要講的是 Bridge 模式。如同 DAY10, DAY11 的 Strategy 模式,我會分做兩篇:案例討論與應用 Bridge 模式以及實際探討何謂 Bridge 模式。
這個例子是這樣的:我們需要要撰寫一個程式,它會使用既存的兩種繪圖程式(DP1
和 DP2
)之一繪製矩形。當實體化矩形後,它會知道該使用哪種繪圖程式。
先講第一種可能的方式。因為需求說到「實體化矩形後,它會知道該使用哪種繪圖程式」,所以我們可以有兩種不同的矩形物件:一種使用 DP1
、一種使用 DP2
。兩種矩形都有自己的繪製方法,但實作方式不同。如圖所示。
當然,作為第一個被提出的可能解法,它本身一定是個有問題的解法。
假設今天新增了圓形,它如同矩形一樣需要透過這兩種繪圖程式進行繪製。同時,我們也要求在繪製過程中,我們無須知道 Rectangle
和 Circle
的差異。
此需求的實作方式其實也很容易,我們可以在增加一層類別,名為 Shape
,而 Rectangle
及 Circle
都是 Shape
的一種。往下,Circle
也做與 Rectangle
一樣的結構設計。最後成果如下圖。
這個方案是個滿直覺的解決方案。當然,它有一些問題存在。我們關注一下這個類別層次中的第三層(V1Rectangle
、V2Rectangle
等)。如果⋯⋯
DP3
,這樣就會從 4 個變做 6 個不同類型的 shape
Shape
,這樣就會有 9 種 Shape
於是類別就會需求的變化有爆炸性的增長。這個現象的原因是因為這個方案中的抽象(Shape
的種類)與實作(繪圖程式)是緊耦合的關係。
這會使我們的程式不易維護。
第一種方案是由於錯誤的繼承層次造成,那麼,用不同的層次呢?舉例來說,不用形狀的差異來把 Shape
來做分類,反而是用使用何種繪圖程式來分類。那麼,實踐出來的 UML 圖會如下。
但如此做也沒有好到哪裡去,類別的爆炸性增長並沒有因此解決。
現在,我們打算引入設計模式到這個問題上,並嘗試解決它。
在劈頭就直接說明 Bridge 模式,並二話不說地拿來套用在上述案例前,不如先試著回想前幾天提到的一些原則,再來看看套用在這個案例上,會如何實踐。
我們會想到基本的兩條策略:
用共通性與可變性分析:用在這案例中,共同的概念會是「形狀」與「繪圖程式」;在變化的則是形狀的種類以及繪圖程式的種類。
共同的部分抽做抽象類別,再將變化的部分封裝成衍生類別繼承於其之下。這麼一來,我們就有下圖的兩個類別層次。
注意到 V1Drawing
與 V2Drawing
還尚未使用到 Dp1
與 DP2
,但我們總是可以拿前者去使用後者(Adapter 模式)。
再來,就讓其中一類使用另外一類(聚合)即可。這裡就有個有趣的問題產生:是要讓形狀使用繪圖程式,還是繪圖程式使用形狀呢?
我們會選用前者。但後者的問題在哪呢?
如果繪圖程式要直接繪製形狀,那麼它必須知道形狀是什麼,這既違反了「物件只對自己負責」的原則,也違反了封裝(類型的隱藏)。
而反觀前者,Shape
物件無須知道使用哪一種 Drawing
的類型,因為可以讓 Shape
參照 Drawing
類別。
最後,再將原本的 Dp1
與 DP2
與我們的介面銜接上,我們就會得到我們的設計:
而這就是我們想要得到的 Bridge 模式!(上例中其實也包含了 adapter 模式)
至於 Bridge 模式的定義與關鍵特徵為何?我們下篇再繼續吧!